package logic

import (
	"context"
	"fmt"
	"github.com/18767156402/x/errors"
	"github.com/go-redis/redis/v8"
	"github.com/golang-module/carbon"
	"github.com/jxskiss/base62"
	"github.com/zeromicro/go-zero/core/logc"
	"github.com/zeromicro/go-zero/core/logx"
	"shortUrl/internal/model"
	"shortUrl/internal/svc"
	"shortUrl/internal/types"
	"strings"
	"time"
)

type ShortenLogic struct {
	logx.Logger
	ctx    context.Context
	svcCtx *svc.ServiceContext
}

func NewShortenLogic(ctx context.Context, svcCtx *svc.ServiceContext) *ShortenLogic {
	return &ShortenLogic{
		Logger: logx.WithContext(ctx),
		ctx:    ctx,
		svcCtx: svcCtx,
	}
}

func (l *ShortenLogic) Shorten(req *types.ShortenReq) (resp *types.ShortenResp, err error) {
	shortUrl := model.ShortUrl{
		RequestId:    req.RequestId,
		LongUrl:      req.LongUrl,
		RedirectCode: req.RedirectCode,
	}
	if req.TTL > 0 {
		expireAt := carbon.Now().AddSeconds(req.TTL).ToStdTime()
		shortUrl.ExpireAt = &expireAt
	}
	result := l.svcCtx.Db.Create(&shortUrl)
	if result.Error != nil {
		return nil, errors.New(1001, "创建短链失败.")
	}
	shortenKey, shortLink := l.buildShortUrl(shortUrl.ID)
	shortUrl.ShortenKey = shortenKey
	shortUrl.ShortUrl = shortLink
	result = l.svcCtx.Db.Model(&shortUrl).Select("ShortenKey", "ShortUrl").Updates(shortUrl)
	if result.Error != nil {
		return nil, errors.New(1001, "创建短链失败!")
	}
	var cacheTTl time.Duration
	if req.TTL > 0 {
		cacheTTl = time.Duration(req.TTL) * time.Second
	} else {
		cacheTTl = time.Hour
	}
	l.syncToCache(shortenKey, shortUrl.LongUrl, shortUrl.RedirectCode, cacheTTl)
	return &types.ShortenResp{ShortUrl: shortUrl.ShortUrl}, nil
}

func (l *ShortenLogic) buildShortUrl(dbRecordPk uint64) (shortenKey, shortUrl string) {
	shortenKey = base62.EncodeToString(base62.FormatUint(dbRecordPk))
	if strings.HasSuffix(l.svcCtx.Business.Domain, "/") {
		shortUrl = l.svcCtx.Business.Domain + shortenKey
	} else {
		shortUrl = l.svcCtx.Business.Domain + "/" + shortenKey
	}
	return shortenKey, shortUrl
}

func (l *ShortenLogic) buildShortUrlCacheKey(shortenKey string) string {
	return fmt.Sprintf("shorten:%s:url", shortenKey)
}

func (l *ShortenLogic) buildShortUrlRedirectCodeCacheKey(shortenKey string) string {
	return fmt.Sprintf("shorten:%s:redirectCode", shortenKey)
}

func (l *ShortenLogic) getShortUrlFromCache(shortenKey string) string {
	var cacheValue string
	var err error
	cacheKey := l.buildShortUrlCacheKey(shortenKey)
	cacheValue, err = l.svcCtx.CacheDb.Get(l.ctx, cacheKey).Result()
	if err == redis.Nil {

	} else if err != nil {
		logc.Errorf(l.ctx, "从缓存去获取内容失败,key:%s,error:%s", cacheKey, err.Error())
	}
	return cacheValue
}

func (l *ShortenLogic) getRedirectCodeFromCache(shortenKey string) int {
	var cacheValue int
	var err error
	cacheKey := l.buildShortUrlRedirectCodeCacheKey(shortenKey)
	cacheValue, err = l.svcCtx.CacheDb.Get(l.ctx, cacheKey).Int()
	if err == redis.Nil {

	} else if err != nil {
		logc.Errorf(l.ctx, "从缓存去获取内容失败,key:%s,error:%s", cacheKey, err.Error())
	}
	return cacheValue
}

func (l *ShortenLogic) syncToCache(shortenKey string, longUrl string, redirectCode int, expiration time.Duration) bool {
	internalCacheKey1 := l.buildShortUrlCacheKey(shortenKey)
	if _, err := l.svcCtx.CacheDb.Set(l.ctx, internalCacheKey1, longUrl, expiration).Result(); err != nil {
		logc.Errorf(l.ctx, "写入缓存失败,key:%s,value:%s", internalCacheKey1, longUrl)
	}
	internalCacheKey2 := l.buildShortUrlRedirectCodeCacheKey(shortenKey)
	if _, err := l.svcCtx.CacheDb.Set(l.ctx, internalCacheKey2, redirectCode, expiration).Result(); err != nil {
		logc.Errorf(l.ctx, "写入缓存失败,key:%s,value:%d", internalCacheKey2, redirectCode)
	}
	return true
}
